﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.ADF;
using System.Reflection;
using ZJGIS.Framework;
using ZJGIS.ADF.BaseClasses;
using System.Runtime.InteropServices;
using Zjgis.Geosoc.Utils.Web;
using System.Threading;
using System.IO;
using System.Xml;

namespace ZJGIS.CoordCovert.model
{
    ///<summary>
    ///作用：坐标变形转换，通过研究院提供的服务接口进行变形
    ///作者：盛琦
    ///编写日期：2013-04-03
    ///</summary>
    public class GeosocConvert : Operator2Ex
    {
        private List<IFeatureClass> pFeatClses;

        private string url;//服务地址

        private int prj;//默认-1。 坐标系方式：可接受-1、0、1三值之一。-1 ——WGS84坐标系； 0 ——北京54坐标系； 1 ——西安80坐标系
        private int zone;//默认为0。分带方式：0——按3度分带 1——按6度分带。
        private double mid;//中央经线，默认为120。

        private double dx;
        private double dy;


        private object missing = Type.Missing;
        /// <summary>
        /// 需要进行转换的要素类集合
        /// </summary>
        public List<IFeatureClass> FeatClses
        {
            set { pFeatClses = value; }
        }

        /// <summary>
        /// 服务地址
        /// </summary>
        public string Url
        {
            set { url = value; }
        }

        /// <summary>
        /// 坐标系方式
        /// </summary>
        public int Prj
        {
            set { prj = value; }
        }

        /// <summary>
        /// 中央经线
        /// </summary>
        public int Zone
        {
            set { zone = value; }
        }

        /// <summary>
        /// λ缩放系数
        /// </summary>
        public double Mid
        {
            set { mid = value; }
        }

        public GeosocConvert()
        {
            prj = -1;
            zone = 0;
            mid = 120;

            dx = dy = 0;
            try
            {
                string configPath = AppDomain.CurrentDomain.BaseDirectory + "Config.xml";

                if (File.Exists(configPath))
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.Load(configPath);

                    XmlNode xmlNode = xmlDoc.SelectSingleNode("//Config//CoordCovert//变形变换");

                    dx = Double.Parse(xmlNode.Attributes["X偏移量"].Value);
                    dy = Double.Parse(xmlNode.Attributes["Y偏移量"].Value);
                }
            }
            catch { }
        }

        public void Convert(double x, double y, ref double X, ref double Y)
        {
            X = x + dx;
            Y = y + dy;
        }

        /// <summary>
        /// 数据转换
        /// </summary>
        /// <returns></returns>
        public override bool Operate()
        {
            if (pFeatClses == null || pFeatClses.Count == 0)
            {
                errLog.Invoke("请设置需要进行变形的数据！");
                return false;
            }

            if (url == "")
            {
                errLog.Invoke("请设置变形转换的服务地址！");
                return false;
            }

            setProcessBarMax.Invoke(pFeatClses.Count);

            int countTotal = 0;
            int errorCountTotal = 0;
            int rightCountTotal = 0;

            try
            {

                for (int i = 0; i < pFeatClses.Count; i++)
                {
                    if (stopAnyway)
                        return false;

                    setProcessBarCurrent.Invoke(i + 1);

                    IFeatureClass pFeatCls = pFeatClses[i];

                    int allCount = pFeatCls.FeatureCount(null);
                    if (allCount == 0) continue;

                    setSubProcessBarMax(allCount);

                    writeLog.Invoke("开始对数据[" + pFeatCls.AliasName + "]进行变形转换...");

                    #region 逐要素转换

                    IFeatureCursor pFeatCur = pFeatCls.Update(null, false);
                    IFeature pFeature = pFeatCur.NextFeature();

                    int count = 0;
                    int rightCount = 0;
                    int errorCount = 0;

                    while (pFeature != null)
                    {
                        if (stopAnyway)
                            return false;

                        count++;
                        SetSubProcessBarCurrent.Invoke(count);


                        if (ConvertFeature(pFeature))
                        {
                            rightCount++;
                        }
                        else
                        {
                            errorCount++;
                        }
                        pFeatCur.UpdateFeature(pFeature);
                        pFeature = pFeatCur.NextFeature();
                    }

                    if (errorCount == 0)
                    {
                        doneLog.Invoke("[" + pFeatCls.AliasName + "]数据转换完毕。待转换总数：" + count + "；成功：" + rightCount + "；失败：" + errorCount + "\n");
                    }
                    else
                    {
                        errLog.Invoke("[" + pFeatCls.AliasName + "]数据转换完毕。待转换总数：" + count + "；成功：" + rightCount + "；失败：" + errorCount + "\n");
                    }

                    countTotal += count;
                    rightCountTotal += rightCount;
                    errorCountTotal += errorCount;

                    Marshal.ReleaseComObject(pFeatCur);

                    #endregion

                    //AddFeatureclassSpatialIndex(pFeatCls, 0, 0, 0);
                }

                if (errorCountTotal == 0)
                {
                    doneLog.Invoke("所有数据转换完毕。待转换总数：" + countTotal + "；成功：" + rightCountTotal + "；失败：" + errorCountTotal + "\n");
                }
                else
                {
                    errLog.Invoke("所有数据转换完毕。待转换总数：" + countTotal + "；成功：" + rightCountTotal + "；失败：" + errorCountTotal + "\n");
                }

                return true;
            }
            catch (Exception ex)
            {
                errLog.Invoke("转换失败：" + ex.Message);
                return false;
            }
        }

        #region private functions

        /// <summary>
        /// 对单个要素进行转换
        /// </summary>
        /// <param name="pFeature"></param>
        /// <returns>转换成功返回true、否则返回false</returns>
        private bool ConvertFeature(IFeature pFeature)
        {
            IGeometry pGeometry = pFeature.ShapeCopy;
            if (pGeometry.IsEmpty) return true;     //如果图形为空，则无需转换

            try
            {
                if (pGeometry.GeometryType == esriGeometryType.esriGeometryMultipoint ||
                    pGeometry.GeometryType == esriGeometryType.esriGeometryPolyline)
                {
                    IPointCollection pPoints = pGeometry as IPointCollection;
                    for (int i = 0; i < pPoints.PointCount; i++)
                    {
                        IPoint pPoint = pPoints.get_Point(i);

                        ConvertPoint(pPoint);

                        pPoints.UpdatePoint(i, pPoint);
                    }

                    
                }
                //如果是面状数据，因为首尾点是同一个点，第一个点做了坐标转换后，最后一个点是不需要再进行转换的，否则该点（首/尾点）就会转换了两次
                else if (pGeometry.GeometryType == esriGeometryType.esriGeometryPolygon)
                {
                    #region 可以较好的转换面图层，但是Z值信息会丢失，转换后全为零
                    IGeometryCollection pGeometryNew = new GeometryBagClass();
                    IPolygon pPolygon = null;
                    ITopologicalOperator2 pTop = null;

                    IGeometryCollection pGeometries = pGeometry as IGeometryCollection;
                    if (pGeometries == null || pGeometries.GeometryCount == 0) return true;

                    bool hasZ = false;
                    IFeatureClass pFeatCls = (pFeature.Class as IFeatureClass);
                    IField pField = pFeatCls.Fields.get_Field(pFeatCls.FindField(pFeatCls.ShapeFieldName));
                    IFieldEdit pFieldEdit = pField as IFieldEdit;
                    IGeometryDef pGeometryDef = pFieldEdit.GeometryDef;
                    hasZ = pGeometryDef.HasZ;

                    for (int i = 0; i < pGeometries.GeometryCount; i++)
                    {
                        IGeometry pGeomLow = pGeometries.get_Geometry(i);
                        IGeometry pGeomHigh = Convertor.ConvertLow2High(pGeomLow);

                        if (hasZ)
                        {
                            pGeomHigh = GeometryUtility.Geom2Dto3D(pGeomHigh);
                        }

                        IPointCollection pPoints = pGeomHigh as IPointCollection;

                        //最后一个点不用转！
                        for (int j = 0; j < pPoints.PointCount - 1; j++)
                        {
                            IPoint pPoint = pPoints.get_Point(j);

                            ConvertPoint(pPoint);

                            pPoints.UpdatePoint(j, pPoint);
                        }

                        if (pPolygon == null)
                        {
                            pPolygon = pGeomHigh as IPolygon;
                            pTop = pPolygon as ITopologicalOperator2;
                        }
                        else
                        {
                            pTop.IsKnownSimple_2 = false;
                            if (!pTop.IsKnownSimple) pTop.Simplify();

                            ITopologicalOperator2 pTopTemp = pGeomHigh as ITopologicalOperator2;
                            pTopTemp.IsKnownSimple_2 = false;
                            if (!pTopTemp.IsKnownSimple) pTopTemp.Simplify();

                            pPolygon = pTop.Union(pGeomHigh) as IPolygon;
                            pTop = pPolygon as ITopologicalOperator2;
                        }
                    }

                    pFeature.Shape = pPolygon;
                    #endregion
                }
                else if (pGeometry.GeometryType == esriGeometryType.esriGeometryPoint)
                {
                    IPoint pPoint = pGeometry as IPoint;

                    ConvertPoint(pPoint);

                    //pFeature.Shape = pPoint;
                }
                else
                {
                    errLog.Invoke("类型不支持！");
                    return false;
                }

                pFeature.Shape = pGeometry;
                //pFeature.Store();

                return true;
            }
            catch (Exception ex)
            {
                errLog.Invoke("对ID为[" + pFeature.OID.ToString() + "]的要素转换失败：" + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 对某个点进行转换
        /// </summary>
        /// <param name="pPoint"></param>
        private void ConvertPoint(IPoint pPoint)
        {
            double x = pPoint.X;
            double y = pPoint.Y;

            string wholeUrl = url + "?xy=" + x.ToString() + "," + y.ToString() + "&prj=" + prj.ToString() + "&zone=" + zone.ToString()
                + "&mid=" + mid.ToString() + "&callback=showresult";

            HttpRequestHelper http = new HttpRequestHelper();
            byte[] result = http.GetGetResponse(wholeUrl);
            
            string rst = System.Text.Encoding.UTF8.GetString(result);

            //如果节点重复太多，则让程序休息10秒钟再运行
            while (rst.Contains("false"))
            {
                Thread.Sleep(10000);
                result = http.GetGetResponse(wholeUrl);
                rst = System.Text.Encoding.UTF8.GetString(result);
            }

            string coord = rst.Substring(rst.IndexOf('\'') + 1);
            coord = coord.Substring(0, coord.IndexOf('\''));

            string[] coords = coord.Split(",".ToCharArray());
            double X = Double.Parse(coords[0]);
            double Y = Double.Parse(coords[1]);

            pPoint.X = X;
            pPoint.Y = Y;

        }

        /// <summary>
        /// 计算四参数
        /// </summary>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <param name="X1"></param>
        /// <param name="Y1"></param>
        /// <param name="X2"></param>
        /// <param name="Y2"></param>
        /// <param name="dx"></param>
        /// <param name="dy"></param>
        /// <param name="alpa"></param>
        /// <param name="namta"></param>
        private void Compute(double x1, double y1, double x2, double y2, double X1, double Y1, double X2, double Y2, out double dx, out double dy, out double alpa, out double namta)
        {
            double TanAlpa = ((Y1 - Y2) * (x1 - x2) - (X1 - X2) * (y1 - y2)) / ((X1 - X2) * (x1 - x2) + (Y1 - Y1) * (y1 - y2));
            alpa = Math.Atan(TanAlpa);

            namta = (X1 - X2) / ((x1 - x2) * Math.Cos(alpa) - ((y1 - y2) * Math.Sin(alpa)));

            dx = X1 - namta * x1 * Math.Cos(alpa) + namta * y1 * Math.Sin(alpa);

            dy = Y1 - namta * x1 * Math.Sin(alpa) - namta * y1 * Math.Cos(alpa);
        }
        private double ComputeTanAlpa(double x1, double y1, double x2, double y2, double X1, double Y1, double X2, double Y2)
        {
            return ((Y1 - Y2) * (x1 - x2) - (X1 - X2) * (y1 - y2)) / ((X1 - X2) * (x1 - x2) + (Y1 - Y1) * (y1 - y2));
        }

        /// <summary>
        /// 删除要素类的空间索引
        /// </summary>
        /// <param name="pFeatureClass"></param>
        /// <returns></returns>
        private bool DeleteFeatureclassSpatialIndex(IFeatureClass pFeatureClass)
        {
            // Get an enumerator for indexes based on the shape field.
            IIndexes indexes = pFeatureClass.Indexes;
            string shapeFieldName = pFeatureClass.ShapeFieldName;
            IEnumIndex enumIndex = indexes.FindIndexesByFieldName(shapeFieldName);
            enumIndex.Reset();

            // Get the index based on the shape field (should only be one) and delete it.
            IIndex index = enumIndex.Next();

            if (index != null)
            {
                pFeatureClass.DeleteIndex(index);
            }

            return true;
        }

        /// <summary>
        /// 添加要素类的空间索引
        /// </summary>
        /// <param name="pFeatureClass"></param>
        /// <param name="pGridOneSize"></param>
        /// <param name="pGridTwoSize"></param>
        /// <param name="pGridThreeSize"></param>
        /// <returns></returns>
        private bool AddFeatureclassSpatialIndex(IFeatureClass pFeatureClass, Double gridOneSize, Double gridTwoSize, Double gridThreeSize)
        {
            // Clone the shape field from the feature class.
            string shapeFieldName = pFeatureClass.ShapeFieldName;
            int shapeFieldIndex = pFeatureClass.FindField(shapeFieldName);
            IFields fields = pFeatureClass.Fields;
            IField sourceField = fields.get_Field(shapeFieldIndex);
            IClone sourceFieldClone = (IClone)sourceField;
            IClone targetFieldClone = sourceFieldClone.Clone();
            IField targetField = (IField)targetFieldClone;

            // Open the geometry definition from the cloned field and modify it.
            IGeometryDef geometryDef = targetField.GeometryDef;
            IGeometryDefEdit geometryDefEdit = (IGeometryDefEdit)geometryDef;
            geometryDefEdit.GridCount_2 = 3;
            geometryDefEdit.set_GridSize(0, gridOneSize);
            geometryDefEdit.set_GridSize(1, gridTwoSize);
            geometryDefEdit.set_GridSize(2, gridThreeSize);

            // Create a spatial index and set the required attributes.
            IIndex newIndex = new IndexClass();
            IIndexEdit newIndexEdit = (IIndexEdit)newIndex;
            newIndexEdit.Name_2 = shapeFieldName + "_Index";
            newIndexEdit.IsAscending_2 = true;
            newIndexEdit.IsUnique_2 = false;

            // Create a fields collection and assign it to the new index.
            IFields newIndexFields = new FieldsClass();
            IFieldsEdit newIndexFieldsEdit = (IFieldsEdit)newIndexFields;
            newIndexFieldsEdit.AddField(targetField);
            newIndexEdit.Fields_2 = newIndexFields;

            // Add the spatial index back into the feature class.
            pFeatureClass.AddIndex(newIndex);

            return true;
        }

        #endregion
    }
}
